/*
 * (C) Copyright 2012, by Tim Xie.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.superapk.diamond.sqllite;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import com.superapk.diamond.sqllite.annotation.DBField;
import com.superapk.diamond.sqllite.annotation.DBTable;
import com.superapk.diamond.util.LogUtil;

/**
 * 数据库表结构信息
 * 
 * @author     Tim Xie [tim.xie2008@gmail.com]
 * @date       2013-3-14
 * 
 */

class TableInfo {
	
	/**
	 * 数据表结构列集合
	 */
	private final static HashMap<String, TableInfo> TABLEINOF_MAP = new HashMap<String, TableInfo>();
	/**
	 * 对应的实例类名
	 */
	private String mClassName;
	/**
	 * 表名
	 */
	private String mTableName;
	/**
	 * 表的主键Id
	 */
	private FieldInfo mPrimaryKey;
	/**
	 * 数据表字段名集合
	 */
	private HashMap<String, FieldInfo> mFieldInfoMap;
	/**
	 * 表示表是否已经存在创建
	 */
	private boolean mExist;
	
	/**
	 * 根据一个Java对象获取
	 * @param c
	 * @return
	 */
	static TableInfo getTableInfo(Class<?> c){
		if(c == null){
			return null;
		}
		TableInfo tableInfo = TABLEINOF_MAP.get(c.getName());
		if(tableInfo == null){
			tableInfo = new TableInfo();
			//获取表名和类名
			String tableName = tableInfo.getTableName(c);
			String className = c.getName();
			tableInfo.setClassName(className);
			tableInfo.setTableName(tableName);
			//获取表的主键
			FieldInfo primaryKeyField = tableInfo.getTableGeneratedId(c);
			tableInfo.setPrimaryKey(primaryKeyField);
			//获取表的所有字段名
			HashMap<String,FieldInfo> fieldInfoMap = tableInfo.getTableColumns(c);
			tableInfo.setFieldInfoMap(fieldInfoMap);
		}
		return tableInfo;
	}
	
	/**
	 * 根据Java对象获取数据库的表名
	 * @param c
	 * @return
	 */
	private String getTableName(Class<?> c){
		String tableName = null;
		DBTable dbTable = null;
		if(c.isAnnotationPresent(DBTable.class)){
			dbTable = (DBTable) c.getAnnotation(DBTable.class);
		}
		if(dbTable == null || dbTable.tableName().trim().equals("")){
			tableName = c.getSimpleName().toLowerCase();
		}else{
			tableName = dbTable.tableName();
		}
		return tableName;
	}
	
	/**
	 * 根据Java对象获取数据库的主键
	 * @param c
	 * @return
	 */
	private FieldInfo getTableGeneratedId(Class<?> c){
		FieldInfo primaryKeyFieldInfo = new FieldInfo();
		Field primaryKeyField = null;
		Field[] fields = c.getDeclaredFields();
		if(fields != null){
			for(Field field : fields){ 
				DBField dbField = field.getAnnotation(DBField.class);
				if(dbField != null){
					//获取主键注解
					if(dbField.generatedId()){
						primaryKeyField = field;
						break;
					}
				}
			}
			//尝试是否有id或者_id的字段名
			if(primaryKeyField == null){
				for(Field field : fields){ 
					if("id".equals(field.getName()) || "_id".equals(field.getName())){
						primaryKeyField = field;
						break;
					}
				}
			}
		}else{
			throw new RuntimeException("this class " + c + " has no field");
		}
		if(primaryKeyField != null){
			primaryKeyFieldInfo.setPrimeKey(true);
			
			DBField dbField = primaryKeyField.getAnnotation(DBField.class);
			//字段名
			String columnName = dbField.column();
			if(columnName != null && !columnName.trim().equals("")){
				primaryKeyFieldInfo.setColumnName(columnName);
			}else{
				columnName = primaryKeyField.getName();
				primaryKeyFieldInfo.setColumnName(columnName);
			}
			//数据类型
			primaryKeyFieldInfo.setDataType(primaryKeyField.getType());
			//FieldName
			primaryKeyFieldInfo.setFieldName(primaryKeyField.getName());
			return primaryKeyFieldInfo;
		}else{
			return null;
		}
	}
	
	/**
	 * 根据Java对象获取数据库的字段集
	 * @param c
	 * @return
	 */
	private HashMap<String,FieldInfo> getTableColumns(Class<?> c){
		HashMap<String,FieldInfo> fieldInfoMap = new HashMap<String,FieldInfo>();
		try{
			Field[] fieldArray = c.getDeclaredFields();
			for(Field f : fieldArray){
				FieldInfo fieldTmp = new FieldInfo();
				
				String fieldName = f.getName();
				fieldTmp.setFieldName(fieldName);
				if(fieldName.equals(mPrimaryKey.getFieldName())){
					//主键，跳过
					continue;
				}
				DBField dbField = f.getAnnotation(DBField.class);
				//字段名
				String columnName = null;
				if(dbField != null){
					columnName = dbField.column();
				}
				if(columnName != null && !columnName.trim().equals("")){
					fieldTmp.setColumnName(columnName);
				}else{
					columnName = f.getName().toLowerCase();
					fieldTmp.setColumnName(columnName);
				}
				//数据类型
				fieldTmp.setDataType(f.getType());
				if(dbField != null){
					//是否唯一
					boolean unique = dbField.unique();
					fieldTmp.setUnique(unique);
					//是否外键
					boolean foreign = dbField.foreign();
					fieldTmp.setForeign(foreign);
					
					// TODO 需要完善外键关联的功能
					
					//默认值
					String defaultValue = dbField.defaultValue();
					if(defaultValue != null && !defaultValue.equals("")){
						fieldTmp.setDefaultValue(defaultValue);
					}
				}
				//Set Get方法
				Method setMethod = getFieldSetMethod(c,f);
				Method getMethod = getFieldGetMethod(c,f);
				fieldTmp.setSet(setMethod);
				fieldTmp.setGet(getMethod);
				
				fieldInfoMap.put(fieldName, fieldTmp);
			}
		}catch(Exception e){
			LogUtil.e(e.toString());
		}
		return fieldInfoMap;
	}
	
	/**
	 * 根据Field获取该属性的set方法
	 * @param c
	 * @param f
	 * @return
	 */
	private Method getFieldSetMethod(Class<?> c, Field f) {
		String fieldName = f.getName();
		String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);;
		try {
			return c.getDeclaredMethod(methodName, f.getType());
		} catch (NoSuchMethodException e) {
			LogUtil.e(e.toString());
		}
		return null;
	}
	
	/**
	 * 根据Field获取该属性的Get或者Is方法
	 * @param c
	 * @param f
	 * @return
	 */
	private Method getFieldGetMethod(Class<?> c, Field f) {
		String fieldName = f.getName();
		String methodName = null;
		Class<?> dataType = f.getType();
		if(dataType == boolean.class || dataType == Boolean.class){
			methodName = "is" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		}else{
			methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		}
		try {
			return c.getDeclaredMethod(methodName);
		} catch (NoSuchMethodException e) {
			LogUtil.e(e.toString());
		}
		return null;
	}

	String getClassName() {
		return mClassName;
	}

	void setClassName(String className) {
		this.mClassName = className;
	}

	String getTableName() {
		return mTableName;
	}

	void setTableName(String tableName) {
		this.mTableName = tableName;
	}

	FieldInfo getPrimaryKey() {
		return mPrimaryKey;
	}

	void setPrimaryKey(FieldInfo fieldInfo) {
		this.mPrimaryKey = fieldInfo;
	}

	boolean isExist() {
		return mExist;
	}

	void setExist(boolean exist) {
		this.mExist = exist;
	}

	HashMap<String, FieldInfo> getFieldInfoMap() {
		return mFieldInfoMap;
	}

	void setFieldInfoMap(HashMap<String, FieldInfo> fieldInfoMap) {
		this.mFieldInfoMap = fieldInfoMap;
	}
	
}
